package nl.utwente.ewi.hmi.multitouch.drivers.image;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import nl.utwente.ewi.hmi.multitouch.SurfaceType;
import nl.utwente.ewi.hmi.multitouch.Tangible;
import nl.utwente.ewi.hmi.multitouch.Touch;
import nl.utwente.ewi.hmi.multitouch.TouchDevice;
import nl.utwente.ewi.hmi.multitouch.TouchDeviceAdapter;
import nl.utwente.ewi.hmi.multitouch.TouchDeviceInfo;
import nl.utwente.ewi.hmi.multitouch.TouchEnvironment;
import nl.utwente.ewi.hmi.multitouch.analysis.Segment;
import nl.utwente.ewi.hmi.multitouch.drivers.mouse.MouseTouchStreamProvider;
import nl.utwente.ewi.hmi.multitouch.event.TouchAdapter;
import nl.utwente.ewi.hmi.multitouch.event.TouchEvent;
import nl.utwente.ewi.hmi.multitouch.io.TouchStream;
import nl.utwente.ewi.hmi.multitouch.io.TouchStreamProvider;

public class ImageTouchStreamProvider implements TouchStreamProvider {

	private TouchDeviceInfo touchDeviceInfo = null;
	
	private ImageTouchStream touchStream = null;
	
	private List<TouchDeviceInfo> infos = null;
	
	public ImageTouchStreamProvider(URL imageLocation, boolean isAmbiguous, boolean wrap) throws IOException {
		File file = null;

		try {
			file = new File(imageLocation.toURI());
		} catch (URISyntaxException e) {
			throw new RuntimeException("Image not available");
		}

		if(!file.exists()) {
			throw new RuntimeException("Image not available");
		}

		ImageInputStream stream = ImageIO.createImageInputStream(file);
		
		Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
		
		if(!readers.hasNext()) {
			throw new RuntimeException("Image not available");
		}
		
		ImageReader reader = readers.next();
		
		reader.setInput(stream);
		int imageCount = reader.getNumImages(true);
		
		Dimension dimension = new Dimension(0, 0);
		
		BufferedImage[] images = new BufferedImage[imageCount];
		
		for(int i = 0; i < imageCount; i++) {
			BufferedImage image = reader.read(i);
			images[i] = image;
			dimension.width = Math.max(image.getWidth(), dimension.width);
			dimension.height = Math.max(image.getHeight(), dimension.height);
		}

		this.touchDeviceInfo = new TouchDeviceInfo(dimension, "image : " + imageLocation.toString(), isAmbiguous);
		this.touchStream = new ImageTouchStream(touchDeviceInfo, images, wrap);
		this.infos = Collections.unmodifiableList(Arrays.asList(new TouchDeviceInfo[] { this.touchDeviceInfo}));
	}
	
	public ImageTouchStreamProvider(URL imageLocation) throws IOException {
		this(imageLocation, false, false);
	}
	
	public ImageTouchStreamProvider(URL imageLocation, boolean isAmbiguous) throws IOException {
		this(imageLocation, isAmbiguous, false);
	}
	
	@Override
	public List<TouchDeviceInfo> getTouchDeviceInfoList() {
		return this.infos;
	}

	@Override
	public TouchStream getTouchStream(TouchDeviceInfo deviceInfo) {
		if(deviceInfo == this.touchDeviceInfo) {
			return this.touchStream;
		}
		return null;
	}

	private static class ImageTouchStream extends TouchStream {

		BufferedImage[] images		= null;
		private int offset			= 0;
		Frame[] frames				= null;
		boolean isOpen				= false;
		TouchType[] touchTypeMap	= null;
		Tangible[] tangibleMap		= null;
		Dimension size				= null;
		
		Set<Object> actors			= new HashSet<Object>();
		
		boolean wrap				= false;
		
		Tangible touch = new Tangible() {
			
			Object actor = null;
			
			{
				actor = new Object();
				actors.add(actor);
			}
			SurfaceType type = SurfaceType.UNKNOWN;
			public Object getActor() {
				return actor;
			}
			public SurfaceType getSurfaceType() {
				return type;
			}
			public boolean canJoinInto(Tangible tangible) {
				return false;
			}
			public boolean canSplitInto(Tangible tangible) {
				return false;
			}
			@Override
			public boolean isUnique() {
				return false;
			}
		};
		
		public ImageTouchStream(TouchDeviceInfo touchDeviceInfo, BufferedImage[] images, boolean wrap) {
			super(touchDeviceInfo);

			this.images = images;
			this.wrap = wrap;
			
			this.size = touchDeviceInfo.getSize();

			this.touchTypeMap = new TouchType[size.width * size.height];
			this.tangibleMap = new Tangible[size.width * size.height];
			
			this.frames = new Frame[] { new Frame(this.size) };
		}

		public boolean close() throws IOException {
			if(this.isOpen) {
				this.isOpen = false;
				return true;
			}

			return false;
		}

		/*
		public Frame[] getFrames() throws IOException {
			if(images.length == offset && !wrap) {
				return this.frames;
			}

			DataBuffer buffer = images[offset].getRaster().getDataBuffer();
			for(int i = 0; i < buffer.getSize(); i++) {
				int val = buffer.getElem(i) & 0xff;
				if(val != 0x00) {
					touchTypeMap[i] = TouchType.POSITIVE;
					tangibleMap[i] = touch;
				} else {
					touchTypeMap[i] = TouchType.NEGATIVE;
					tangibleMap[i] = null;
				}
			}

			frames[0].writeTangibleMap(tangibleMap);
			frames[0].writeTouchTypeMap(touchTypeMap);
			
			offset++;
			if(wrap) {
				offset %= this.images.length;
			}
			return this.frames;
		}
*/
		public boolean isOpen() {
			return this.isOpen;
		}

		public boolean open() throws IOException {
			if(!this.isOpen) {
				this.offset = 0;
				this.isOpen = true;
				return true;
			}

			return false;
		}

		@Override
		public Set<Object> getActors() {
			return Collections.unmodifiableSet(this.actors);
		}

		@Override
		public void read(Set<Segment> segments) throws IOException {
			// TODO Auto-generated method stub
			
		}
		
	}
	
	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		URL url = ClassLoader.getSystemClassLoader().getResource("simple_touch.gif");
		TouchStreamProvider itsp = new ImageTouchStreamProvider(url);
		
		itsp = MouseTouchStreamProvider.getMouseTouchStreamProvider();
		
		
		TouchEnvironment te = TouchEnvironment.getTouchEnvironment();
		te.registerTouchStreamProvider(itsp);
		TouchDevice device = te.getTouchDevice(te.getTouchDeviceInfoList().get(0));
		device.addTouchDeviceListener(new TouchDeviceAdapter() {
			
			@Override
			public void touchDeviceStarted(TouchDevice touchDevice) {
				System.out.println("started");
			}
			
			@Override
			public void touchRegistered(TouchDevice touchDevice, Touch touch) {
				System.out.println("touched!");
				touch.addTouchListener(new TouchAdapter() {
					@Override
					public void touchReleased(TouchEvent event) {
						System.out.println("and released");
					}
					
					@Override
					public void touchMoved(TouchEvent event) {
						System.out.println("moved");
					}
				});
			}
			
		});

		device.start();
		
		Object obj = new Object();

		synchronized(obj) {
			obj.wait();
		}

		
	}
}
